/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_PERSIST_NAMESPACE_PROPERTY_TREE_WRITER_HPP
#define MOE_PERSIST_NAMESPACE_PROPERTY_TREE_WRITER_HPP

#include "DefaultNamespaceTreeFormat.hpp"
#include "PropertyTreeWriter.hpp"
#include "../ObjectTypeManager.h"
#include "../Name/Namespace.h"

namespace MOE
{

class ClassMeta;
class DataMemberMeta;

namespace Persist
{

template <class TreeType, template <class> class TreeFormatPolicy = DefaultNamespaceTreeFormat>
class NamespacePropertyTreeWriter
{
protected:
	struct NamespacePropertyTreeWriterVisitor
	{
		NamespacePropertyTreeWriterVisitor(const Name::Namespace* parentNamespace, TreeType& objList)
			: _parentNamespace(parentNamespace)
			, _objListTree(objList)
			, _objWriter(parentNamespace)
			, _success(true)
		{
		}

		void OnObject(const RawByteVector& childName, const ClassIDType classID, Address objAddr)
		{
			// Put down an "object" subtree.
			TreeType& objTree = _objListTree.add_child(TreeFormatPolicy<TreeType>::ObjectField(), TreeType());

			// Write type and name.
			ClassMetaProxy metaProxy = ObjectTypeManager::GetSingleton().GetClassMeta(classID);
			if (!metaProxy.IsValid())
			{
				_success = false;
				return;
			}

			const ClassMeta& meta = metaProxy;
			objTree.put(TreeFormatPolicy<TreeType>::ObjectTypeField(), meta.GetClassName().to_string<TreeType::data_type>());
			objTree.put(TreeFormatPolicy<TreeType>::ObjectNameField(), childName.to_string<TreeType::data_type>());

			// Write object.
			TreeType& valueTree = objTree.add_child(TreeFormatPolicy<TreeType>::ObjectValueField(), TreeType());
			_success &= _objWriter.Write(meta, objAddr, valueTree);
		}

		void OnNamespace(const RawByteVector&, Name::Namespace*)
		{
			// Do nothing for child namespaces
		}

		const Name::Namespace* _parentNamespace;
		TreeType& _objListTree;
		PropertyTreeWriter<TreeType, TreeFormatPolicy> _objWriter;
		bool _success;
	};

public:
	NamespacePropertyTreeWriter()
	{
	}

	bool Write(TreeType& ptree, const Name::Namespace* namespacePtr = 0) const
	{
		if (!namespacePtr)
		{
			namespacePtr = &Name::Namespace::GetGlobalNamespace();
		}

		if (!namespacePtr)
		{
			return false;
		}

		// Write the namespace entry.
		ptree.put(TreeFormatPolicy<TreeType>::NamespaceField(), namespacePtr->GetFullPath<TreeType::data_type::value_type>().to_string<TreeType::data_type>());

		// Write the object list and iterate through all children.
		// We only write all OBJECTS in this namespace.
		// Child namespaces have to be written by calling Write on them.
		// TODO: Consider writing a utility function to do that for the users.

		TreeType& objList = ptree.add_child(TreeFormatPolicy<TreeType>::ObjectListField(), TreeType());
		NamespacePropertyTreeWriterVisitor visitor(namespacePtr, objList);

		namespacePtr->Visit(visitor);

		return visitor._success;
	}
};

}

}

#endif
